package org.appchat.service.protobuf;

/**
 * Created by Ryan of app-chat.org
 * on: 7/11/12 at 10:58 AM
 */

import com.google.protobuf.Message;
import org.appchat.api.Listener;
import org.appchat.service.ReflectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * This is ugly generics/reflection code that will create an UnMarshaller
 * for any {@link com.google.protobuf.Message}, and can UnMarshall
 * the message with the type from a byte[] array.
 *
 * @param <M> The type of message to unmarshall
 */

public class MessageUnMarshaller<M extends Message> {

    /**
     * The parseFrom(byte[]) {@link Method} from the {@link Message}, that takes a byte[] and
     * returns an {@link Message} generated from the byte[] array.
     */

    private final Method parseFromBytes;

    /**
     * Private constructor that is used to construct the unmarshaller for a parametrized type.
     *
     * @param type The {@link Class} type of message.
     * @throws NoSuchMethodException
     */

    private MessageUnMarshaller(Class<M> type) throws NoSuchMethodException {
        parseFromBytes = type.getMethod("parseFrom", byte[].class);
    }

    /**
     * Creates an instance of the MessageUnMarshaller for type {@link P}
     *
     * @param listener The {@link Listener} implementation you need a MessageUnMarshaller for
     * @param <P>  The type of {@link Message} you need an MessageUnMarshaller for
     * @return {@link MessageUnMarshaller} object with parametrized type {@link P}
     * @throws NoSuchMethodException
     */

    @SuppressWarnings("unchecked")//The cast from Class<?> to Class<P> is type safe because P extends Message.
    public static <P extends Message> MessageUnMarshaller<P> getUnMarshaller(Listener listener) throws NoSuchMethodException {
        Class<P> type = (Class<P>) ReflectionUtils.getParameterizedTypeFromInterface(listener);
        return new MessageUnMarshaller<P>(type);
    }

    /**
     * Constructs an {@link Message} from a byte[] array.
     *
     * @param message The byte[] array from the {@link javax.jms.BytesMessage}
     * @return An {@link Message} from the byte array.
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */

    @SuppressWarnings("unchecked")//Cast to type M is safe due to bounded parametrized types. 
    public M unMarshall(byte[] message) throws InvocationTargetException, IllegalAccessException {
        return (M) parseFromBytes.invoke(null, message);
    }
}
